﻿using System.Collections.Concurrent;
using System.Net.Sockets;
using Net.Public.Msg.Package;


namespace Net.Public.Msg
{
    /// <summary>
    /// 接受信息并进行合并的工具类，避免tcp的粘包
    /// 记录一条信息，当接收完整的时候，进行合并，并抛出一条事件
    /// 每个实体（前后端）都应该包含这么一个消息接受的功能类
    /// </summary>
    public class MsgHandler
    {
        /// <summary>
        /// 默认1m的缓存 超过1m抛出异常，并丢弃消息
        /// </summary>
        private int _msgLen = 1024 * 1024;

        private ConcurrentQueue<byte> _cacheMsgQueue;
        private int _cacheMsgIndex = 0;
        private int nowLen = 0;
        private IPackage _packMgr;


        public void ReceiveMsg(byte[] msgBytes, int receiveNum)
        {
            if (receiveNum > 0)
            {
                //获得前receiveNum个字节的字节数组
                byte[] receivedBytes = new byte[receiveNum];
                Array.Copy(msgBytes, receivedBytes, receiveNum);
                CacheByteAdd(receivedBytes);
            }
        }

        public void SendMsg<T>(Socket socket, T msg) where T : BaseMsg
        {
            byte[] _ = _packMgr.PackMsg(msg);
            socket.Send(_);
        }
        
        /// <summary>
        /// 防止粘包做的处理，外部需要每帧调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public (BaseMsg,bool) HandlerMsg() 
        {
            if (_cacheMsgQueue.Count <= 0) return default;
            // 表示当前没有消息
            if (nowLen == 0)
            {
                // 尝试取信息头
                if (_cacheMsgQueue.Count >= 4)
                {
                    byte[] temp = GetCacheMsgBytes(4);
                    // 取四个字节的数据，并赋值给nowLen -4是因为包含信息头
                    nowLen = BitConverter.ToInt32(temp, 0) - 4;
                }
            }

            //表示已经读取到了信息长度，接下俩就是获取信息体
            if (nowLen > 0)
            {
                if (_cacheMsgQueue.Count >= nowLen)
                {
                    //获取对应长度的信息体
                    List<byte> msgByteList = new List<byte>(BitConverter.GetBytes(nowLen + 4));
                    byte[] res = GetCacheMsgBytes(nowLen);
                    foreach (var _ in res)
                    {
                        msgByteList.Add(_);
                    }

                    nowLen = 0;
                    var unPackMsg = _packMgr.UnPackMsg<BaseMsg>(msgByteList.ToArray());
                    return (unPackMsg,true);
                }
            }

            return default;
        }


        #region 生命周期

        public MsgHandler()
        {
            _cacheMsgQueue = new();
            _packMgr = new TcpPackHandler();
        }

        public void Close()
        {
            _cacheMsgQueue = null;
        }

        #endregion

        #region 内部私有实现
        
        /// <summary>
        /// 将res添加到缓存字节数组中
        /// 起始位置是_nowMsgCacheIndex
        /// </summary>
        /// <param name="res"></param>
        private void CacheByteAdd(byte[] res)
        {
            for (int i = 0; i < res.Length; i++)
            {
                _cacheMsgQueue.Enqueue(res[i]);
            }
        }

        private byte[] GetCacheMsgBytes(int cnt)
        {
            if (cnt > _cacheMsgQueue.Count) return null;
            byte[] bytes = new byte[cnt];
            for (int i = 0; i < cnt; i++)
            {
                _cacheMsgQueue.TryDequeue(out bytes[i]);
            }

            return bytes;
        }

        #endregion
    }
}